package server;

import java.util.ArrayList;

import datastructure.PatientRecord;

/**
 * A class which handles the validity of the input strings. Controls the hash,
 * nounce and timestamp before sending requests to the database.
 * 
 * @author dt08sj7
 * 
 */
public class SecurityLayer {

	private ConnectionClientJDBC db;
	private final String user, password, url;
	private final NounceHandler nounceHandler;

	/**
	 * Creates a security layer to the database.
	 * 
	 * @param user
	 *            the user name to the database.
	 * @param password
	 *            the password to the database.
	 * @param url
	 *            the url to the database.
	 */
	public SecurityLayer(String user, String password, String url) {
		this.user = user;
		this.password = password;
		this.url = url;
		nounceHandler = new NounceHandler();
	}

	/**
	 * Authorizes the entered ssn with the given hash.
	 * 
	 * @param ssn
	 *            the ssn of the user.
	 * @param hash
	 *            the hash of the user.
	 * @return true if the user is authorized.
	 */
	public boolean authorize(String ssn, String hash) {
		System.out.println("Authorize: " + ssn + "  " + hash);
		System.out.println(db.getHash(ssn));
		System.out.println(hash.equals(db.getHash(ssn)));
		return hash.equals(db.getHash(ssn));
	}

	/**
	 * Opens connection to the database.
	 */
	public void opendbConnection() {

		if (db == null) {
			db = new ConnectionClientJDBC(user, password, url);
		}

		if (!db.isConnected()) {
			db.openConnection();
		}

	}

	/**
	 * Authorizes and fetches all records available for the user.
	 * 
	 * @param ssn
	 *            the ssn of the user.
	 * @param hash
	 *            the hash of the user.
	 * @param logString
	 *            the string to be logged.
	 * @param timeStamp
	 *            the timestamp of the package.
	 * @param nounce
	 *            the nounce of the package.
	 * @return a string with all records.
	 */
	public String readRecords(String ssn, String hash, String logString,
			long timeStamp, String nounce) {
		if (!checkTimeValidity(timeStamp)) {
			opendbConnection();
			db.log(logString + "Gammalt packet, replay attack?", ssn);
			return "NACK";
		} else if (!checkNounce(nounce)) {
			return "NACK";
		}

		opendbConnection();
		if (authorize(ssn, hash)) {
			return recordsInStringFormat(ssn);
		}

		return "NACK";

	}

	/**
	 * 
	 * Authorizes and appends a records for the user.
	 * 
	 * @param ssn
	 *            the ssn of the user.
	 * @param hash
	 *            the hash of the user.
	 * @param logString
	 *            the string to be logged.
	 * @param data
	 *            the data to be appended to the record.
	 * @param timeStamp
	 *            the timestamp of the package.
	 * @param nounce
	 *            the nounce of the package.
	 * @return a string with all records.
	 */
	public String appendRecord(String ssn, String hash, String logString,
			String data, long timeStamp, String nounce) {
		System.out.println("appendrecordi secLayer");
		if (!checkTimeValidity(timeStamp)) {
			opendbConnection();
			db.log(logString + "Gammalt packet, replay attack?", ssn);
			return "NACK";
		} else if (!checkNounce(nounce)) {
			return "NACK";
		}

		opendbConnection();
		if (authorize(ssn, hash)) {

			if (db.append(parseToPatientRecord(data), ssn)) {
				return "ACK";
			}

		}

		return "NACK";

	}

	/**
	 * Authorizes and deletes a record.
	 * 
	 * @param ssn
	 *            the ssn of the user.
	 * @param hash
	 *            the hash of the user.
	 * @param logString
	 *            the string to be logged.
	 * @param data
	 *            the number of the record t obe deleted.
	 * @param timeStamp
	 *            the timestamp of the package.
	 * @param nounce
	 *            the nounce of the package.
	 * @return Return "ACK" if the package where removed, else "NACK".
	 */
	public String deleteRecord(String ssn, String hash, String logString,
			String data, long timeStamp, String nounce) {
		if (!checkTimeValidity(timeStamp)) {
			opendbConnection();
			db.log(logString + "Gammalt packet, replay attack?", ssn);
			System.out.println("Replay attack varning");
			return "NACK";
		} else if (!checkNounce(nounce)) {
			return "NACK";
		}

		opendbConnection();
		if (authorize(ssn, hash)) {

			if (db.deleteRecord(Integer.parseInt(data), ssn)) {

				return "ACK";
			}

		}
		return "NACK";

	}

	/**
	 * Authorizes and fetches the hash of a user.
	 * 
	 * @param ssn
	 *            the user which hash is desired.
	 * @param logString
	 *            the string to be logged.
	 * @param timeStamp
	 *            the timestamp of the package.
	 * @param nounce
	 *            the nounce of the package.
	 * @return a string with the user hash, or "NACK" if the request failed.
	 */
	public String getHash(String ssn, String hash, String logString,
			long timeStamp, String nounce) {
		if (!checkTimeValidity(timeStamp)) {
			opendbConnection();
			db.log(logString + "Gammalt packet, replay attack?", ssn);
			return "NACK";
		} else if (!checkNounce(nounce)) {
			return "NACK";
		}

		opendbConnection();
		if (authorize(ssn, hash)) {
			return "ACK";
		}
		return "NACK";

	}

	/**
	 * Authorizes and deletes all records of a specific patient.
	 * 
	 * @param ssn
	 *            the ssn of the one who issues this call.
	 * @param hash
	 *            the hash of the issuer.
	 * @param logString
	 *            the string to be logged.
	 * @param data
	 *            the ssn of the patient.
	 * @param timeStamp
	 *            the timestamp of the package.
	 * @param nounce
	 *            the nounce of the package.
	 * @return "ACK" if the request was successful, else "NACK".
	 */
	public String deleteAllRecords(String ssn, String hash, String logString,
			String data, long timeStamp, String nounce) {
		if (!checkTimeValidity(timeStamp)) {
			opendbConnection();
			db.log(logString + "Gammalt packet, replay attack?", ssn);
			return "NACK";
		} else if (!checkNounce(nounce)) {
			return "NACK";
		}

		opendbConnection();

		if (authorize(ssn, hash)) {

			if (db.deleteAllRecords(data, ssn)) {

				return "ACK";
			}

		}
		return "NACK";

	}

	/**
	 * Creates a string from an arraylist which contains patient records.
	 * 
	 * @param record
	 *            the arraylist.
	 * @return a string of the arraylist.
	 */
	private String parseFromArrayToString(ArrayList<PatientRecord> record) {
		StringBuffer sb = new StringBuffer();

		if (!(record == null)) {
			for (int i = 0; i < record.size(); i++) {
				sb.append(record.get(i).toClientString());

				if (i != (record.size() - 1)) {
					sb.append(";");
				}
			}

			return sb.toString();
		}
		return "";

	}

	/**
	 * Parses a string to a patientrecord.
	 * 
	 * @param data
	 *            the string to be parsed.
	 * @return the data string in a patient record.
	 * 
	 */
	private PatientRecord parseToPatientRecord(String data) {
		System.out.println(data);
		String[] cmdDataFragments = data.split(":");
		for (int i = 0; i < 6; i++) {
			System.out.println(cmdDataFragments[i]);
		}

		PatientRecord patientRecord = new PatientRecord(cmdDataFragments[0],
				cmdDataFragments[1], cmdDataFragments[2], cmdDataFragments[3],
				cmdDataFragments[4], cmdDataFragments[5]);

		return patientRecord;

	}

	/**
	 * Fetches all records available to a certain ssn and creates a string of
	 * the patient records.
	 * 
	 * @param ssn
	 * @return
	 */
	private String recordsInStringFormat(String ssn) {
		System.out.println("recordsInStringFormat.");
		return parseFromArrayToString(db.getAuthorizedRecords(ssn));
	}

	/**
	 * Controls if the timestamp is within the acceptable time frame
	 * 
	 * @param timeStamp
	 *            the timestamp to be controlled.
	 * @return true if the timestamp is valid, else false.
	 */
	private boolean checkTimeValidity(long timeStamp) {
		System.out.println("Checktimestamp...");
		long currentTime = System.currentTimeMillis();
		long diff = currentTime - timeStamp;
		System.out.println("Timestamp diff:" + diff);
		return (diff <= 5000);

	}

	/**
	 * Authorizes and returns a new nounce.
	 * 
	 * @param timeStamp
	 *            The timestamp to validated.
	 * @return a string with the nounce.
	 */
	public String getNewNounce(String ssn, String hash, long timeStamp) {
		if (!checkTimeValidity(timeStamp)) {
			return "NACK";
		}

		opendbConnection();
		if (authorize(ssn, hash)) {
			return nounceHandler.getNewNounce();
		}
		return "NACK";
	}

	/**
	 * Verifies the nounce.
	 * 
	 * @param nounce
	 *            the nounce to be verified.
	 * @return true if the nounce is valid, else false.
	 */
	private boolean checkNounce(String nounce) {
		return nounceHandler.validateNounce(nounce);
	}
}
